const Graph = ForceGraph3D({
    extraRenderers: [new THREE.CSS2DRenderer()],
})(document.getElementById('graph'))
    .backgroundColor('#00000000')
    .jsonUrl('./src/data.json');


const scene = Graph.scene();
const camera = Graph.camera();
const renderer = Graph.renderer();

console.log(scene)
console.log(renderer)



const graphObjs = {
    spriteMaterial:{},
    clock: new THREE.Clock(),
};
graphObjs.clock.start();


Graph.nodeThreeObject((node) => getNodeObject(node))

    // .dagMode('td')
    .dagMode(null)
    .dagLevelDistance(50)

    .linkThreeObject(link => getLinkObject(link))

    .linkPositionUpdate( (line, {start, end}) => updateLinkObject(line, {start, end}) );


const fxaaPass = new THREE.SMAAPass(window.innerWidth * renderer.getPixelRatio(), window.innerHeight * renderer.getPixelRatio());
Graph.postProcessingComposer().addPass(fxaaPass);


let i = 0;
document.addEventListener('keyup', function () {
    i++;
    if(i % 2 === 0)
        Graph.dagMode('td');
    else
        Graph.dagMode(null);
});



/**
 * 初始化
 */
(function init() {
    let mainNodeTexture = new THREE.TextureLoader().load( `./src/img/紫球.png`);
    let mainNodeMaterial = new THREE.SpriteMaterial({map: mainNodeTexture});

    let nodeTexture = new THREE.TextureLoader().load( `./src/img/蓝球.png`);
    let nodeMaterial = new THREE.SpriteMaterial({map: nodeTexture});

    let miniNodeTexture = new THREE.TextureLoader().load( `./src/img/小球.png`);
    let miniNodeMaterial = new THREE.SpriteMaterial({map: miniNodeTexture});


    let resolution = new THREE.Vector2();
    renderer.getSize(resolution);

    const faceCameraMaterial = new THREE.ShaderMaterial( {

        uniforms: {
            center: { value: new THREE.Vector3() },
            start: { value: new THREE.Vector3() },
            end: { value: new THREE.Vector3() },
            startColor: { value: new THREE.Color() },
            endColor: { value: new THREE.Color() },
            time: { value: 0 },

            resolution: { value: resolution }
        },

        vertexShader: document.getElementById( 'vs' ).textContent,

        fragmentShader: document.getElementById( 'fs' ).textContent,

        wireframe: false,

    } );



    graphObjs.spriteMaterial = {
        mainNodeMaterial,
        nodeMaterial,
        miniNodeMaterial
    };
    graphObjs.faceCameraMaterial = faceCameraMaterial;


})();


/**
 * 获取节点几何体
 */
function getNodeObject(node) {

    let material, scale;
    switch (node.type) {
        case 'mainNode':
            material = graphObjs.spriteMaterial.mainNodeMaterial;
            scale = 15;
            break;
        case 'node':
            material = graphObjs.spriteMaterial.nodeMaterial;
            scale = 10;
            break;
        default:
            material = graphObjs.spriteMaterial.miniNodeMaterial;
            scale = 6;
    }

    const sprite = new THREE.Sprite(material);
    sprite.scale.set(scale, scale, scale);
    sprite.scaleSize = scale * 1.2;

    const moonMassDiv = document.createElement('div');
    moonMassDiv.className = 'label';
    moonMassDiv.textContent = node.text;

    if(node.type === 'miniNode') moonMassDiv.style.marginTop = '1em';
    const moonMassLabel = new THREE.CSS2DObject(moonMassDiv);
    sprite.add(moonMassLabel);




    //mainNode 添加光环
    if(node.type === 'mainNode'){
        const geometry = new THREE.TorusGeometry( 0.7, 0.01, 3, 48 );
        const material = new THREE.MeshBasicMaterial( { color: 0xe1a9dd, depthTest: false } );
        const torus = new THREE.Mesh( geometry, material );

        const geometry_sphere = new THREE.SphereGeometry( 0.04, 12, 12 );
        const material_sphere = new THREE.MeshBasicMaterial( { color: 0x49a2ee, depthTest: false } );
        const sphere = new THREE.Mesh( geometry_sphere, material_sphere );
        sphere.position.set(0.7, 0, 0);

        // let helper = new THREE.AxesHelper(100);
        // torus.add(helper);
        torus.add(sphere)

        torus.onBeforeRender = function () {
            torus.rotateY(Math.PI / 360);
            torus.rotateZ(Math.PI / 180);
        }

        const torus2 = torus.clone();
        torus2.scale.set(0.85, 0.85, 0.85);
        torus2.onBeforeRender = function () {
            torus2.rotateX(Math.PI / 150);
            torus2.rotateZ(-Math.PI / 180);
        }



        sprite.add( torus );
        sprite.add( torus2 );

        sprite.scaleSize = scale * 1.5;
    }






    return sprite;
}

/**
 * 获取连接线几何体
 * @param link
 */
function getLinkObject(link) {

    // console.log(link);



    const geometry = new THREE.BufferGeometry;
    const vIndex = new Float32Array( [ 0, 1, 2, 3 ] );
    const indices = [0, 2, 1, 0, 3, 2];

    const vertices = new Float32Array( [
        -1.0, -1.0,  1.0,
        1.0, -1.0,  1.0,
        1.0,  1.0,  1.0,
        1.0,  1.0,  1.0,
    ] );

    geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );

    geometry.setIndex( indices);
    geometry.setAttribute( 'vIndex', new THREE.BufferAttribute( vIndex, 1 ) );
    // console.log(geometry);




    const material = graphObjs.faceCameraMaterial.clone();



    // const material = new THREE.LineBasicMaterial({
    //     color: 0x0000ff
    // });
    // const material = new THREE.MeshBasicMaterial({
    //     side: THREE.DoubleSide,
    //     color: 0xff0000,
    //     wireframe: true
    // });

    const linkObject = new THREE.Mesh( geometry, material );
    linkObject.frustumCulled = false;

    linkObject.onBeforeRender = function () {
        linkObject.material.uniforms.time.value = graphObjs.clock.getElapsedTime() % 1;
    }


    return linkObject;
}

/**
 * 更新箭头
 * @param line
 * @param start
 * @param end
 * @returns {boolean}
 */
function updateLinkObject(line, {start, end}) {
    let startVec = new THREE.Vector3(start.x, start.y, start.z);
    let endVec = new THREE.Vector3(end.x, end.y, end.z);

    const startNodeObj = line.__data.source.__threeObj;
    const endNodeObj = line.__data.target.__threeObj;
    const startNodeSize = startNodeObj.scaleSize;
    const endNodeSize = endNodeObj.scaleSize;


    const direction = endVec.clone().sub(start).normalize();
    const newStart = startVec.clone().addScaledVector(direction, startNodeSize / 2);
    const newEnd = endVec.clone().addScaledVector(direction, -endNodeSize / 2);


    let center = new THREE.Vector3().addVectors(newStart, newEnd).divideScalar(2);
    line.material.uniforms.center.value.copy(center);
    line.material.uniforms.start.value.copy(newStart);
    line.material.uniforms.end.value.copy(newEnd);

    line.boundingBox = new THREE.Box3(newStart, newEnd);


    if(!line.material.hasSetColorUniforms){
        let startColor = getNodeColor(line.__data.source);
        let endColor = getNodeColor(line.__data.target);
        line.material.uniforms.startColor.value.copy(startColor);
        line.material.uniforms.endColor.value.copy(endColor);
        line.material.hasSetColorUniforms = true;
    }

    return true;
}


/**
 * 获取节点对应的颜色
 * @param node
 * @returns {Color|Color}
 */
function getNodeColor(node) {

    switch (node.type) {
        case 'mainNode':
            return new THREE.Color(0xea4cf4);
        case 'node':
            return new THREE.Color(0x3b71f6);
        case 'miniNode':
            return new THREE.Color(0x60c6ec);
    }
}